package com.pai4j.common.service;

import com.pai4j.common.config.properties.MinioProperties;
import com.pai4j.common.enums.FileTypeEnum;
import com.pai4j.common.exception.BusinessException;
import com.pai4j.common.util.DateUtils;
import com.pai4j.common.util.IDGenerateUtil;
import com.pai4j.domain.vo.response.UploadResultVO;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.UploadObjectArgs;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 *  将文件上传到Minio服务器
 *
 * @author: CYM-pai
 * @date: 2025/04/27 09:00
 **/

@Service
public class MinioService {

    /**
     * 日志
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MinioService.class);

    @Resource
    private MinioClient minioClient;

    @Resource
    private MinioProperties properties;


    /**
     * 图片默认上传路径
     */
    private static final String IMAGE_PKG = "images";

    /**
     * 定义图片和视频的MIME类型列表
     */
    private static final List<String> IMAGE_MIME_TYPES = Arrays.asList("image/jpeg", "image/png", "image/gif", "image/bmp");
    private static final List<String> VIDEO_MIME_TYPES = Arrays.asList("video/mp4", "video/quicktime", "video/x-msvideo", "video/3gpp", "video/3gpp2", "video/webm", "video/ogg");

    /**
     * 上传文件到Minio服务器
     *
     * @param file
     * @return
     */
    public boolean upload(File file) {
        String filename = file.getName();
        try {
            minioClient.uploadObject(UploadObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(filename)
                    .filename(file.getAbsolutePath())
                    .build());
            return true;
        } catch (Exception e) {
            LOGGER.error("file upload minio exception, file name: {}", filename, e);
            return false;
        }
    }

    /**
     * 前端的文件上传
     *
     * @param file
     * @return
     */
    public boolean upload(MultipartFile file) {
        String filename = file.getOriginalFilename();
        try {
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(filename)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(file.getContentType())
                    .build());
            return true;
        } catch (Exception e) {
            LOGGER.error("file upload minio exception, file name: {}", filename, e);
            return false;
        }
    }

    /**
     * 上传到Minio指定路径
     *
     * @param path
     * @param file
     * @return
     */
    public boolean upload(String path, MultipartFile file) {
        String filename = file.getOriginalFilename();
        try {
            filename = path + filename;
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(filename)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(file.getContentType())
                    .build());
            return true;
        } catch (Exception e) {
            LOGGER.error("file upload minio exception, file name: {}", filename, e);
            return false;
        }
    }

    /**
     * 上传到Minio指定目录，可选择自定义随机文件名
     *
     * @param path
     * @param file
     * @param isRandom
     * @return
     */
    public UploadResultVO upload(String path, MultipartFile file, Boolean isRandom) {
        String filename = file.getOriginalFilename();
        if (Boolean.TRUE.equals(isRandom)) {
            long millis = System.currentTimeMillis();
            String extension = filename.substring(filename.lastIndexOf(".") + 1);
            filename = millis + "." + extension;
        }
        try {
            filename = path + "/" + filename;
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(filename)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(file.getContentType())
                    .build());
            String resourceUrl = properties.getEndpoint() + "/" + properties.getBucketName() + "/" + filename;
            return UploadResultVO.of(resourceUrl, fileType(file), filename);
        } catch (Exception e) {
            LOGGER.error("file upload minio exception, file name: {}, source file name: {}", filename, file.getOriginalFilename(), e);
            return null;
        }
    }

    /**
     * 上传BufferedImage到minio存储
     * @param image
     * @return
     */
    public UploadResultVO upload(BufferedImage image) {
        Assert.notNull(image, "image must not be null");
        String timePackage = DateUtils.getDate2String(DateUtils.YYYYMMDD, new Date());
        String imageName = IDGenerateUtil.uuid() + ".jpg";
        // 上传字节数组到 MinIO
        try {
            // 将 BufferedImage 转换为字节数组（以JPEG格式）
            byte[] imageBytes = convertBufferedImageToByteArray(image);
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(IMAGE_PKG + "/" + timePackage + "/" + imageName)
                    .stream(new ByteArrayInputStream(imageBytes), imageBytes.length, -1)
                    .contentType("image/jpeg")
                    .build());
            String resourceUrl = properties.getEndpoint() + "/" + properties.getBucketName() + "/" + IMAGE_PKG + "/" + timePackage + "/" + imageName;
            return UploadResultVO.of(resourceUrl, FileTypeEnum.IMAGE.name(), imageName);
        } catch (Exception e) {
            throw new BusinessException("图片上传失败");
        }
    }

    private static byte[] convertBufferedImageToByteArray(BufferedImage image) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 将 BufferedImage 写入 ByteArrayOutputStream
        ImageIO.write(image, "jpeg", baos);
        baos.flush();
        byte[] imageBytes = baos.toByteArray();
        baos.close();
        return imageBytes;
    }

    private String fileType(MultipartFile file) {
        // 获取文件的MIME类型
        String mimeType = file.getContentType();
        // 发布动态上传文件仅支持图片和视频
        if (IMAGE_MIME_TYPES.contains(mimeType)) {
            return FileTypeEnum.IMAGE.name();
        }
        if (VIDEO_MIME_TYPES.contains(mimeType)) {
            return FileTypeEnum.VIDEO.name();
        }
        return FileTypeEnum.OTHER.name();
    }

    /**
     * 文件下载
     *
     * @param filename
     * @param response
     */
    public void download(String filename, HttpServletResponse response) {
        response.reset();
        try(InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
                .bucket(properties.getBucketName())
                .object(filename)
                .build())) {
            filename = filename.substring(filename.lastIndexOf("/") + 1);
            ServletOutputStream outputStream = response.getOutputStream();
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("utf-8");
            response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
            byte[] bytes = new byte[1024];
            int len;
            while ((len = inputStream.read(bytes)) > 0) {
                outputStream.write(bytes, 0, len);
            }
            outputStream.close();
        } catch (Exception e) {
            LOGGER.error("file download from minio exception, file name: {}", filename,  e);
        }
    }
}